﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Common;
using System.Xml;
using System.Text.RegularExpressions;
using System.Net;
using System.IO;
using System.Threading;

namespace DriverVera2
{
    [System.AddIn.AddIn("DriverVera2")]
    public class DriverVera2 : Common.ModuleBase
    {
        public static string HaDeviceService = "urn:micasaverde-com:serviceId:HaDevice1";
        public static string ZWaveDeviceService = "urn:micasaverde-com:serviceId:ZWaveDevice1";
        private string gatewayIP;
        private LinkedList<Device> deviceList = new LinkedList<Device>();
        private LinkedList<Dimmer> dimmers = new LinkedList<Dimmer>();
        private LinkedList<Hvac> thermostats = new LinkedList<Hvac>();
        private LinkedList<Switch> switches = new LinkedList<Switch>();
        private ClampMeter clampMeter = null;
        private List<View.VPort> ports = new List<View.VPort>();

        public override void Start()
        {
            try
            {
                // initialize your module here
                //
                // make sure that the control never falls out of this function
                // otherwise the module will be unloaded
                // so, if your module doesn't do anything active but reacts to events from other places
                // use  "System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite)" as the last
                // line of this function
                if (moduleInfo.Args().Length > 0)
                {
                    string[] args = moduleInfo.Args();
                    gatewayIP = args[0];
                }
                else
                {
                    StreamReader reader = new StreamReader("config.txt");
                    string line = null;
                    for (int i = 0; i < 5; i++)
                    {
                        line = reader.ReadLine();
                    }
                    gatewayIP = line;
                    reader.Close();
                }
                populateDeviceList();
                populateServices();
                //startmodules();
                registerPorts();
                logger.Log("Vera Zwave Gateway initialized.");
                logger.Log("\nVera Ports:");
                foreach (View.VPort port in GetAllPortsFromPlatform())
                {
                    string[] infos = { port.GetInfo().GetFriendlyName() };
                    logger.Log("{0}", infos);
                    foreach (View.VRole role in port.GetInfo().GetRoles())
                    {
                        string[] roleInfos = { role.Name(), role.ToString() };
                        logger.Log("    {0} -- {1}", roleInfos);
                    }
                }

                System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite);
            }
            catch (Exception e)
            {
                logger.Log("{0}", e.Source);
                logger.Log("{0}", e.Message);
                logger.Log("{0}", e.StackTrace);
            }
        }

        private void registerPorts()
        {

            int counter = 0;

            logger.Log("Registering Ports with Platform");

            foreach (var dim in dimmers)
            {
                var dimmer = dim;
                OperationDelegate opDelegate = delegate(string roleName, string opName, IList<View.VParamType> list)
                {
                    string[] strings = { opName, roleName };
                    //logger.Log("{0} called on {1}", strings);
                    if (roleName == RoleEnergyMeter.RoleName && dimmer.IsMeter)
                    {
                        return EnergyMeter.OnOperationInvoke(roleName, opName, list, dimmer.url, dimmer.deviceID, logger);
                    }
                    else if (roleName == RoleEnergyMeter.RoleName && !dimmer.IsMeter)
                    {
                        throw new Exception("!!!Cannot get meter data because " + dimmer.deviceUUID.ToString() + " is not a meter!!!");
                    }
                    else
                    {
                        return dimmer.OnOperationInvoke(roleName, opName, list);
                    }
                };

                Port dimmerPort;

                List<View.VRole> listRole = new List<View.VRole>();

                RoleSwitchMultiLevel dimmerRole = new RoleSwitchMultiLevel();
                listRole.Add(dimmerRole);

                if (dimmer.IsMeter)
                {
                    listRole.Add(new RoleEnergyMeter());
                }

                //View.VPortInfo portInfo = GetPortInfoFromPlatform("VeraDimmer::" + dimmer.deviceID);
                View.VPortInfo portInfo = GetPortInfoFromPlatform(dimmer.deviceUUID);
                dimmerPort = InitPort(portInfo);
                BindRoles(dimmerPort, listRole, opDelegate);
                RegisterPortWithPlatform(dimmerPort);
                counter++;
                string[] dimmerRegisterLogParams = { dimmer.deviceID.ToString(), portInfo.GetFriendlyName() };
                logger.Log("Registered VeraDimmer::{0} With FriendlyName '{1}'", dimmerRegisterLogParams);

            }

            logger.Log("{0} Dimmers Registered", counter.ToString());
            counter = 0;

            foreach (var therm in thermostats)
            {
                var thermostat = therm;
                OperationDelegate opDelegate = delegate(string roleName, string opName, IList<View.VParamType> list)
                {
                    string[] strings = { opName, roleName };
                    //logger.Log("{0} called on {1}", strings);
                    if (roleName == RoleEnergyMeter.RoleName && thermostat.IsMeter)
                    {
                        return EnergyMeter.OnOperationInvoke(roleName, opName, list, thermostat.url, thermostat.deviceID, logger);
                    }
                    else if (roleName == RoleEnergyMeter.RoleName && !thermostat.IsMeter)
                    {
                        throw new Exception("!!!Cannot get meter data because " + thermostat.deviceUUID.ToString() + " is not a meter!!!");
                    }
                    else
                    {
                        return thermostat.OnOperationInvoke(roleName, opName, list);
                    }
                };

                Port hvacPort;

                List<View.VRole> listRole = new List<View.VRole>();

                RoleHvac hvacRole = new RoleHvac();
                listRole.Add(hvacRole);

                if (thermostat.IsMeter)
                {
                    listRole.Add(new RoleEnergyMeter());
                }

                //View.VPortInfo portInfo = GetPortInfoFromPlatform("VeraHvac::" + thermostat.deviceID);
                View.VPortInfo portInfo = GetPortInfoFromPlatform(thermostat.deviceUUID);
                hvacPort = InitPort(portInfo);
                BindRoles(hvacPort, listRole, opDelegate);
                RegisterPortWithPlatform(hvacPort);
                counter++;
                string[] hvacRegisterLogParams = { thermostat.deviceID.ToString(), portInfo.GetFriendlyName() };
                logger.Log("Registered VeraHvac::{0} With FriendlyName '{1}'", hvacRegisterLogParams);
                //new Thread(new ParameterizedThreadStart(thermostat.subscription)).Start(hvacPort);

            }

            logger.Log("{0} Thermostats Registered", counter.ToString());
            counter = 0;

            foreach (var binSwitch in switches)
            {
                //logger.Log("A");
                var tmpSwitch = binSwitch;
                //logger.Log("B");
                OperationDelegate opDelegate = delegate(string roleName, string opName, IList<View.VParamType> list)
                {
                    //logger.Log("C");
                    string[] strings = { opName, roleName };
                    //logger.Log("{0} called on {1}", strings);
                    //logger.Log("D");
                    if (roleName == RoleEnergyMeter.RoleName && tmpSwitch.IsMeter)
                    {
                        //logger.Log("E");
                        return EnergyMeter.OnOperationInvoke(roleName, opName, list, tmpSwitch.url, tmpSwitch.deviceID, logger);
                    }
                    else if (roleName == RoleEnergyMeter.RoleName && !tmpSwitch.IsMeter)
                    {
                        //logger.Log("F");
                        throw new Exception("!!!Cannot get meter data because " + tmpSwitch.deviceUUID.ToString() + " is not a meter!!!");
                    }
                    else
                    {
                        //logger.Log("G");
                        return tmpSwitch.OnOperationInvoke(roleName, opName, list);
                    }
                };

                Port switchPort;

                List<View.VRole> listRole = new List<View.VRole>();

                RoleSwitchBinary switchRole = new RoleSwitchBinary();
                listRole.Add(switchRole);

                if (tmpSwitch.IsMeter)
                {
                    listRole.Add(new RoleEnergyMeter());
                }

                //View.VPortInfo portInfo = GetPortInfoFromPlatform("VeraSwitch::" + tmpSwitch.deviceID);
                View.VPortInfo portInfo = GetPortInfoFromPlatform(tmpSwitch.deviceUUID);
                switchPort = InitPort(portInfo);
                logger.Log("0");
                BindRoles(switchPort, listRole, opDelegate);
                logger.Log("1");
                RegisterPortWithPlatform(switchPort);
                counter++;
                string[] switchRegisterLogParams = { tmpSwitch.deviceID.ToString(), portInfo.GetFriendlyName() };
                logger.Log("Registered VeraSwitch::{0} With FriendlyName '{1}'", switchRegisterLogParams);

            }

            logger.Log("{0} Binary Switches Registered", counter.ToString());
            counter = 0;

            if (clampMeter != null)
            {
                var clamp = clampMeter;

                OperationDelegate opDelegate = delegate(string roleName, string opName, IList<View.VParamType> list)
                {
                    return clamp.OnOperationInvoke(roleName, opName, list);
                };

                Port clampport;

                List<View.VRole> listRole = new List<View.VRole>();
                RoleClampMeter clamprole = new RoleClampMeter();
                listRole.Add(clamprole);
                View.VPortInfo portinfo = GetPortInfoFromPlatform(clamp.deviceUUID);
                clampport = InitPort(portinfo);
                //logger.Log("0");
                try
                {
                    BindRoles(clampport, listRole, opDelegate);
                }
                catch(Exception e)
                {
                    logger.Log("{0}",e.Source);
                    logger.Log("{0}", e.Message);
                    logger.Log("{0}", e.StackTrace);
                }
                RegisterPortWithPlatform(clampport);
                counter++;
                logger.Log("Registered ClampMeter with FriendlyName '{0}'", portinfo.GetFriendlyName());
            }

            logger.Log("{0} Clamp Meter Registered", counter.ToString());
            counter = 0;
        }

        private void populateDeviceList()
        {
            string url = "http://" + gatewayIP + ":3480/data_request?id=user_data&output_format=xml";
            XmlTextReader reader = new XmlTextReader(url);
            reader.WhitespaceHandling = WhitespaceHandling.Significant;
            while (reader.Read())
            {
                if (reader.NodeType.ToString().Equals("Element"))
                {
                    if (reader.Name.Equals("device"))
                    {
                        Device device = new Device();
                        device.deviceID = int.Parse(reader.GetAttribute("id"));
                        //device.deviceRoom = int.Parse(reader.GetAttribute("room"));
                        device.deviceXML = reader.GetAttribute("device_file");
                        device.deviceUUID = reader.GetAttribute("time_created");
                        device.deviceFriendlyName = reader.GetAttribute("name");
                        device.IsMeter = false;
                        deviceList.AddLast(device);
                        string[] strings = { device.deviceID.ToString(), device.deviceXML };
                        logger.Log("Added Device #{0} with file {1}", strings);
                        reader.Read();
                        if (reader.Name.Equals("states"))
                        {
                            reader.Read();
                            while (reader.Name.Equals("state"))
                            {
                                string service = reader.GetAttribute("service");
                                if (service != null)
                                {
                                    if (Regex.IsMatch(service, @"EnergyMetering1"))
                                    {
                                        device.IsMeter = true;
                                        logger.Log("Device #{0} is an energy meter", device.deviceID.ToString());
                                        break;
                                    }
                                }
                                reader.Read();
                            }
                        }
                    }
                }
            }
        }

        private void populateServices()
        {
            //populateDimmers();
            //populateLocks();
            //populateThermostats();

            //int i = 0;
            string url = "http://" + gatewayIP + ":3480/";
            foreach (var item in deviceList)
            {
                if (Regex.Match(item.deviceXML, @"DimmableLight1").Success)
                {
                    Dimmer dimmer = new Dimmer(item, url, logger);
                    dimmers.AddLast(dimmer);
                    dimmer.IsMeter = item.IsMeter;
                    logger.Log("Device #{0} is a Dimmer", item.deviceID.ToString());
                    //i++;
                }

                else if (Regex.Match(item.deviceXML, @"HVAC_ZoneThermostat1").Success)
                {
                    Hvac hvac = new Hvac(item, url, logger);
                    thermostats.AddLast(hvac);
                    hvac.IsMeter = item.IsMeter;
                    logger.Log("Device #{0} is a Thermostat", item.deviceID.ToString());
                    //i++;
                }

                else if (Regex.Match(item.deviceXML, @"BinaryLight1").Success)
                {
                    Switch binarySwitch = new Switch(item, url, logger);
                    switches.AddLast(binarySwitch);
                    binarySwitch.IsMeter = item.IsMeter;
                    logger.Log("Device#{0} is a Binary Switch", item.deviceID.ToString());
                }
                else if (Regex.Match(item.deviceXML, @"PowerMeter1").Success)
                {
                    if (clampMeter == null)
                    {
                        clampMeter = new ClampMeter(item, url, logger);
                    }
                    else
                    {
                        clampMeter.add(item);
                    }
                    logger.Log("Device#{0} is a Clamp Meter", item.deviceID.ToString());
                }
            }
        }

        public override void Stop()
        {
            // take any cleanup actions here
            throw new NotImplementedException();
        }

        private class Device 
        {
            public int deviceID { get; set; }
            public string deviceXML { get; set; }
            public string deviceUUID { get; set; }
            public bool IsMeter { get; set; }
            public string deviceFriendlyName { get; set; }
        }

        private class Dimmer 
        {
            public string deviceFriendlyName { get; set; }
            public string url { get; set; }
            public int deviceID { get; set; }
            public int targetValue { get; set; }
            public int currentValue { get { return getDimmerValue(); } }
            public bool IsMeter { get; set; }
            public string deviceUUID { get; set; }
            ulong urlTrash = 0;
            View.VLogger logger;

            public Dimmer(Device device, string url, View.VLogger logger)
            {
                this.logger = logger;
                this.url = url;
                this.deviceID = device.deviceID;
                this.deviceUUID = device.deviceUUID;
                targetValue = currentValue;
                this.deviceFriendlyName = device.deviceFriendlyName;
            }

            public IList<View.VParamType> OnOperationInvoke(string roleName, string opName, IList<View.VParamType> list)
            {
                List<View.VParamType> outList = new List<View.VParamType>();

                if (roleName != "switchmultilevel") { throw new Exception("Invalid Role"); }
                try
                {
                    switch (opName)
                    {
                        case RoleSwitchMultiLevel.OpGetName:
                            //byte b = (byte)currentValue;
                            ParamType p = new ParamType(ParamType.SimpleType.integer, "8", currentValue, "level");
                            outList.Add(p);
                            break;
                        case RoleSwitchMultiLevel.OpSetName:
                            setDimmerValue(Convert.ToByte(list[0].Value()));
                            
                            break;
                        default:
                            throw new Exception("Invalid Operation");
                    }
                }
                catch (Exception e)
                {
                    logger.Log("{0}", e.Message);
                }
                return outList;
            }

            private void setDimmerValue(int p)
            {
                string command = url + "data_request?id=lu_action&output_format=xml&DeviceNum=" + deviceID
                    + "&serviceId=urn:upnp-org:serviceId:Dimming1&action=SetLoadLevelTarget&newLoadlevelTarget="
                    + p + "&trash=" + urlTrash;
                urlTrash++;
                Uri uri = new Uri(command, UriKind.Absolute);
                HttpWebRequest cmd = (HttpWebRequest)WebRequest.Create(uri);
                HttpWebResponse res = null;
                HttpWebResponse res2 = null;

                try
                {
                    res = (HttpWebResponse)cmd.GetResponse();
                    res.Close();
                } 
                catch (WebException)
                {
                    try 
                    { 
                        res2 = (HttpWebResponse)cmd.GetResponse(); res2.Close(); 
                    }
                    catch (WebException) 
                    { 
                        logger.Log("Device #{0} Failed to respond",deviceID.ToString());
                        try { res.Close(); }
                        catch { }
                        try { res2.Close(); }
                        catch { }
                    }
                }
            }

            private int getDimmerValue()
            {
                HttpWebResponse res = null;
                int retVal = 0;
                try
                {
                    string command = url + "/data_request?id=lu_variableget&DeviceNum=" + deviceID
                        + "&serviceId=urn:upnp-org:serviceId:Dimming1&Variable=LoadLevelStatus";
                    HttpWebRequest req = (HttpWebRequest)WebRequest.Create(command);
                    res = (HttpWebResponse)req.GetResponse();

                    retVal = int.Parse(new StreamReader(res.GetResponseStream()).ReadLine());
                    res.Close();
                }
                catch
                {
                    try
                    {
                        res.Close();
                    }
                    catch { }
                }

                return retVal;
            }
        
        }

        private class Hvac
        {
            public Hvac(Device device, string url, View.VLogger logger)
            {
                this.url = url;
                this.deviceID = device.deviceID;
                this.deviceXML = device.deviceXML;
                this.logger = logger;
                this.deviceUUID = device.deviceUUID;
                this.deviceFriendlyName = device.deviceFriendlyName;
            }

            public IList<View.VParamType> OnOperationInvoke(string roleName, string opName, IList<View.VParamType> list)
            {
                List<View.VParamType> outList = new List<View.VParamType>();

                if (roleName != RoleHvac.RoleName) { throw new Exception("Invalid Role"); }

                try
                {
                    switch (opName)
                    {

                        case RoleHvac.OpGetCurrentTemp:
                            {
                                byte b = (byte)CurrentTemp;
                                ParamType p = new ParamType(ParamType.SimpleType.integer, "8", b, "currenttemp");
                                outList.Add(p);
                            }
                            break;
                        case RoleHvac.OpGetHeatSetting:
                            {
                                byte b = (byte)HeatTarget;
                                ParamType p = new ParamType(ParamType.SimpleType.integer, "8", b, "heattarget");
                                outList.Add(p);
                            }
                            break;
                        case RoleHvac.OpGetCoolSetting:
                            {
                                byte b = (byte)CoolTarget;
                                ParamType p = new ParamType(ParamType.SimpleType.integer, "8", b, "cooltarget");
                                outList.Add(p);
                            }
                            break;
                        case RoleHvac.OpGetOpMode:
                            {
                                ParamType p = new ParamType(ParamType.SimpleType.text, "", OpMode, "opmode");
                                outList.Add(p);
                            }
                            break;
                        case RoleHvac.OpGetFanMode:
                            {
                                ParamType p = new ParamType(ParamType.SimpleType.text, "", FanMode, "fanmode");
                                outList.Add(p);
                            }
                            break;
                        case RoleHvac.OpGetOpState:
                            {
                                ParamType p = new ParamType(ParamType.SimpleType.text, "", OpState, "opstate");
                                outList.Add(p);
                            }
                            break;

                        case RoleHvac.OpSetHeatSetting:
                            {
                                HeatTarget = Convert.ToInt32(list[0].Value());
                            }
                            break;
                        case RoleHvac.OpSetCoolSetting:
                            {
                                CoolTarget = Convert.ToInt32(list[0].Value());
                            }
                            break;
                        case RoleHvac.OpSetOpMode:
                            {
                                OpMode = Convert.ToString(list[0].Value());
                            }
                            break;
                        case RoleHvac.OpSetFanMode:
                            {
                                FanMode = Convert.ToString(list[0].Value());
                            }
                            break;
                        default:
                            throw new Exception("Invalid Operation");
                    }
                }
                catch (Exception e)
                {
                    logger.Log("{0}", e.Message);
                }

                return outList;
            }

            public void subscription(object portObject)
            {
                Port port = (Port)portObject;
                logger.Log("Started Subscription Service");
                while (true)
                {
                    {
                        List<View.VParamType> retVals = new List<View.VParamType>();
                        retVals.Add(new ParamType(ParamType.SimpleType.integer,"8",(byte)HeatTarget,"heattarget"));
                        port.Notify(RoleHvac.RoleName,RoleHvac.OpGetHeatSetting,retVals);
                    }
                    {
                        List<View.VParamType> retVals = new List<View.VParamType>();
                        retVals.Add(new ParamType(ParamType.SimpleType.integer,"8",(byte)CoolTarget,"cooltarget"));
                        port.Notify(RoleHvac.RoleName,RoleHvac.OpGetCoolSetting,retVals);
                    }
                    {
                        List<View.VParamType> retVals = new List<View.VParamType>();
                        retVals.Add(new ParamType(ParamType.SimpleType.integer,"8",(byte)CurrentTemp,"currenttemp"));
                        port.Notify(RoleHvac.RoleName,RoleHvac.OpGetCurrentTemp,retVals);
                    }
                    {
                        List<View.VParamType> retVals = new List<View.VParamType>();
                        retVals.Add(new ParamType(ParamType.SimpleType.text,"",OpMode,"opmode"));
                        port.Notify(RoleHvac.RoleName,RoleHvac.OpGetOpMode,retVals);
                    }
                    {
                        List<View.VParamType> retVals = new List<View.VParamType>();
                        retVals.Add(new ParamType(ParamType.SimpleType.text,"",OpState,"opstate"));
                        port.Notify(RoleHvac.RoleName,RoleHvac.OpGetOpState,retVals);
                    }
                    {
                        List<View.VParamType> retVals = new List<View.VParamType>();
                        retVals.Add(new ParamType(ParamType.SimpleType.text,"",FanMode,"fanmode"));
                        port.Notify(RoleHvac.RoleName,RoleHvac.OpGetFanMode,retVals);
                    }
                    logger.Log("Published update");
                    Thread.Sleep(10000);
                }
            }

            
            public int CurrentTemp { get { return getCurrentTemp(); } }
            public int HeatTarget { get { return getHeatTarget(); } set { setHeatTarget(value); } }
            public int CoolTarget { get { return getCoolTarget(); } set { setCoolTarget(value); } }
            public string OpMode { get { return getOpMode(); } set { setOpMode(value); } }
            public string OpState { get { return getOpState(); } }
            public string FanMode { get { return getFanMode(); } set { setFanMode(value); } }

            private View.VLogger logger;
            public string url;
            public int deviceID { get; set; }
            public string deviceXML { get; set; }
            public bool IsMeter { get; set; }
            public string deviceUUID { get; set; }
            public string deviceFriendlyName { get; set; }

            private const string sensorService = "urn:upnp-org:serviceId:TemperatureSensor1";
            private const string heatTargetService = "urn:upnp-org:serviceId:TemperatureSetpoint1_Heat";
            private const string coolTargetService = "urn:upnp-org:serviceId:TemperatureSetpoint1_Cool";
            private const string opService = "urn:upnp-org:serviceId:HVAC_UserOperatingMode1";
            private const string fanService = "urn:upnp-org:serviceId:HVAC_FanOperatingMode1";

            private int getCurrentTemp() 
            {
                int retVal = 0;
                HttpWebResponse res = null;

                try
                {
                    string command = url + "/data_request?id=lu_variableget&DeviceNum=" + deviceID
                        + "&serviceId=" + sensorService + "&Variable=CurrentTemperature";
                    HttpWebRequest req = (HttpWebRequest)WebRequest.Create(command);
                    res = (HttpWebResponse)req.GetResponse();

                    retVal = int.Parse(new StreamReader(res.GetResponseStream()).ReadLine());
                    res.Close();
                }
                catch
                {
                    try { res.Close(); }
                    catch { }
                }
                return retVal;
            }

            private int getHeatTarget()
            {
                int retVal = 0;
                HttpWebResponse res = null;
                try
                {
                    string command = url + "/data_request?id=lu_variableget&DeviceNum=" + deviceID
                        + "&serviceId=" + heatTargetService + "&Variable=CurrentSetpoint";
                    HttpWebRequest req = (HttpWebRequest)WebRequest.Create(command);
                    res = (HttpWebResponse)req.GetResponse();

                    retVal = int.Parse(new StreamReader(res.GetResponseStream()).ReadLine());
                    res.Close();
                }
                catch
                {
                    try { res.Close(); }
                    catch { }
                }
                return retVal;
            }

            private int getCoolTarget()
            {
                int retVal = 0;
                HttpWebResponse res = null;
                try
                {
                    string command = url + "/data_request?id=lu_variableget&DeviceNum=" + deviceID
                        + "&serviceId=" + coolTargetService + "&Variable=CurrentSetpoint";
                    HttpWebRequest req = (HttpWebRequest)WebRequest.Create(command);
                    res = (HttpWebResponse)req.GetResponse();

                    retVal = int.Parse(new StreamReader(res.GetResponseStream()).ReadLine());
                    res.Close();
                }
                catch
                {
                    try { res.Close(); }
                    catch { }
                }
                return retVal;
            }

            private string getOpMode()
            {
                string retVal = "";
                HttpWebResponse res = null;
                try
                {
                    string command = url + "/data_request?id=lu_variableget&DeviceNum=" + deviceID
                        + "&serviceId=" + opService + "&Variable=ModeStatus";
                    HttpWebRequest req = (HttpWebRequest)WebRequest.Create(command);
                    res = (HttpWebResponse)req.GetResponse();

                    retVal = new StreamReader(res.GetResponseStream()).ReadLine();
                    res.Close();
                }
                catch
                {
                    try { res.Close(); }
                    catch { }
                }
                return retVal;
            }

            private string getOpState()
            {
                string retVal = "";
                HttpWebResponse res = null;
                try
                {
                    string command = url + "/data_request?id=lu_variableget&DeviceNum=" + deviceID
                        + "&serviceId=" + opService + "&Variable=ModeState";
                    HttpWebRequest req = (HttpWebRequest)WebRequest.Create(command);
                    res = (HttpWebResponse)req.GetResponse();

                    retVal = new StreamReader(res.GetResponseStream()).ReadLine();
                    res.Close();
                }
                catch
                {
                    try { res.Close(); }
                    catch { }
                }
                return retVal;
            }

            private string getFanMode()
            {
                string retVal = "";
                HttpWebResponse res = null;
                try
                {
                    string command = url + "/data_request?id=lu_variableget&DeviceNum=" + deviceID
                        + "&serviceId=" + fanService + "&Variable=ModeStatus";
                    HttpWebRequest req = (HttpWebRequest)WebRequest.Create(command);
                    res = (HttpWebResponse)req.GetResponse();

                    retVal = new StreamReader(res.GetResponseStream()).ReadLine();
                    res.Close();
                }
                catch
                {
                    try { res.Close(); }
                    catch { }
                }
                return retVal;
            }

            private void setHeatTarget(int target)
            {
                string command = url + "data_request?id=lu_action&output_format=xml&DeviceNum=" + deviceID
                    + "&serviceId=" + heatTargetService + "&action=SetCurrentSetpoint&NewCurrentSetpoint=" + target;

                send(command);
            }

            private void setCoolTarget(int target)
            {
                string command = url + "data_request?id=lu_action&output_format=xml&DeviceNum=" + deviceID
                    + "&serviceId=" + coolTargetService + "&action=SetCurrentSetpoint&NewCurrentSetpoint=" + target;

                send(command);
            }

            private void setOpMode(string mode)
            {
                string command = url + "data_request?id=lu_action&output_format=xml&DeviceNum=" + deviceID
                    + "&serviceId=" + opService + "&action=SetModeTarget&NewModeTarget="+ mode;

                send(command);
            }

            private void setFanMode(string mode)
            {
                string command = url + "data_request?id=lu_action&output_format=xml&DeviceNum=" + deviceID
                    + "&serviceId=" + fanService + "&action=SetMode&NewMode=" + mode;

                send(command);
            }

            private void send(string command)
            {
                Uri uri = new Uri(command, UriKind.Absolute);
                HttpWebRequest cmd = (HttpWebRequest)WebRequest.Create(uri);
                HttpWebResponse res = null;

                try
                {
                    res = (HttpWebResponse)cmd.GetResponse();
                    res.Close();
                }
                catch (WebException)
                {
                    try { res = (HttpWebResponse)cmd.GetResponse(); res.Close();  }
                    catch (WebException) 
                    { 
                        logger.Log("Failed to send set command");
                        try { res.Close(); }
                        catch { }
                    }
                }
            }
        }

        private class Switch
        {
            public string deviceFriendlyName { get; set; }
            public string url { get; set; }
            public int deviceID { get; set; }
            public bool State { get { return currentState(); } set { flipTo(value); } }
            public bool IsMeter { get; set; }
            public string deviceUUID { get; set; }
            View.VLogger logger;

            //private int pollOK = 0;

            public Switch(Device device, string url, View.VLogger logger)
            {
                this.logger = logger;
                this.url = url;
                this.deviceID = device.deviceID;
                this.deviceUUID = device.deviceUUID;
                this.deviceFriendlyName = device.deviceFriendlyName;
            }

            public IList<View.VParamType> OnOperationInvoke(string roleName, string opName, IList<View.VParamType> list)
            {
                List<View.VParamType> outList = new List<View.VParamType>();

                if (roleName != RoleSwitchBinary.RoleName) { throw new Exception("Invalid Role"); }

                switch (opName)
                {
                    case (RoleSwitchBinary.OpGetName):
                        outList.Add(new ParamType(ParamType.SimpleType.binary, "", State, "state"));
                        break;
                    case (RoleSwitchBinary.OpSetName):
                        State = Convert.ToBoolean(list[0].Value());
                        break;
                    default:
                        throw new Exception("Invalid Operation");
                }

                return outList;
            }

            private string switchService = "urn:upnp-org:serviceId:SwitchPower1";

            private bool currentState()
            {
                /*string pollOKcmd = url + "/data_request?id=lu_variableget&DeviceNum=" + deviceID
                    + "&serviceId=" + "urn:micasaverde-com:serviceId:ZWaveDevice1" + "&Variable=PollOk";
                HttpWebResponse pokres = null;
                int pokret = -1;
                try
                {
                    HttpWebRequest pokreq = (HttpWebRequest)WebRequest.Create(pollOKcmd);
                    pokres = (HttpWebResponse)pokreq.GetResponse();
                    pokret = int.Parse(new StreamReader(pokres.GetResponseStream()).ReadLine());
                    pokres.Close();
                }
                catch (Exception e)
                {
                    logger.Log("{0} {1}", "PollOk not accessible on Device:", deviceID.ToString());
                    logger.Log("{0}", e.Source);
                    logger.Log("{0}", e.Message);
                    logger.Log("{0}", e.StackTrace);
                    try { pokres.Close(); }
                    catch { }
                }

                if (pokret == pollOK)
                {
                    return false;
                }
                else
                {
                    pollOK = pokret;
                }*/

                string command = url + "/data_request?id=lu_variableget&DeviceNum=" + deviceID
                    + "&serviceId=" + switchService + "&Variable=Status";
                HttpWebResponse res = null;
                int retVal = 0;
                try
                {
                    HttpWebRequest req = (HttpWebRequest)WebRequest.Create(command);
                    res = (HttpWebResponse)req.GetResponse();

                    retVal = int.Parse(new StreamReader(res.GetResponseStream()).ReadLine());
                    res.Close();
                }
                catch (Exception e)
                {
                    logger.Log("{0}", e.Message);
                    try { res.Close(); }
                    catch { }
                }

                if (retVal == 0) { return false; }
                else { return true; }
            }

            private void flipTo(bool value)
            {
                string newState;
                if (value) { newState = "1"; }
                else { newState = "0"; }
                HttpWebResponse res = null;

                string command = url + "data_request?id=lu_action&output_format=xml&DeviceNum=" + deviceID
                    + "&serviceId=" + switchService + "&action=SetTarget&newTargetValue=" + newState;
                try
                {
                    HttpWebRequest req = (HttpWebRequest)WebRequest.Create(command);
                    res = (HttpWebResponse)req.GetResponse();
                    res.Close();
                }
                catch (Exception e)
                {
                    logger.Log("{0}", e.Message);
                    try { res.Close(); }
                    catch { }
                }
            }
        }

        private class ClampMeter
        {
            public bool IsMeter { get { return false; } }
            public string deviceFriendlyName { get; set; }
            public string url { get; set; }
            public Dictionary<int, bool> subDevices = new Dictionary<int, bool>();
            public string deviceUUID { get; set; }
            View.VLogger logger;


            public ClampMeter(Device device, string url, View.VLogger logger)
            {
                this.logger = logger;
                this.url = url;
                subDevices.Add(device.deviceID, device.IsMeter);
                this.deviceUUID = device.deviceUUID;
                this.deviceFriendlyName = device.deviceFriendlyName;
            }

            public IList<View.VParamType> OnOperationInvoke(string roleName, string opName, IList<View.VParamType> list)
            {
                List<View.VParamType> retVals = new List<View.VParamType>();
                if (roleName != RoleClampMeter.RoleName) { throw new Exception("Invalid Role"); }

                switch (opName)
                {
                    case (RoleClampMeter.OpGetUseRate):
                        retVals.Add(new ParamType(ParamType.SimpleType.integer, "32", callEnergyMetering(RoleEnergyMeter.OpGetUseRate), ""));
                        break;
                    case (RoleClampMeter.OpGetUseTotal):
                        retVals.Add(new ParamType(ParamType.SimpleType.integer, "32", callEnergyMetering(RoleEnergyMeter.OpGetUseTotal), ""));
                        break;
                }

                return retVals;
            }

            private int callEnergyMetering(string operation)
            {
                int total = 0;
                foreach (int key in subDevices.Keys)
                {
                    bool found;
                    subDevices.TryGetValue(key, out found);
                    if (found)
                    {
                        IList<View.VParamType> ret = EnergyMeter.OnOperationInvoke(RoleEnergyMeter.RoleName, operation, null, url, key, logger);
                        total += Convert.ToInt32(ret[0].Value());
                    }
                }
                total = total / 2;
                return total;
            }

            internal void add(Device item)
            {
                subDevices.Add(item.deviceID, item.IsMeter);
            }
        }

        private class EnergyMeter
        {
            public static IList<View.VParamType> OnOperationInvoke(string roleName, string opName, IList<View.VParamType> list,
                string url, int deviceID, View.VLogger logger)
            {

                bool connectionQuery = false;
                string command = url + "data_request?id=lu_variableget&DeviceNum=" + deviceID
                    + "&serviceId=" + meterService + "&Variable=";
                string isOffQuery = url + "data_request?id=lu_variableget&DeviceNum=" + deviceID
                    + "&serviceId=" + DriverVera2.ZWaveDeviceService + "&Variable=PollOk";
                switch (opName)
                {
                    case (RoleEnergyMeter.OpGetUseRate):
                        command += "Watts";
                        break;
                    case (RoleEnergyMeter.OpGetUseTotal):
                        command += "KWH";
                        break;
                    case (RoleEnergyMeter.OpGetConnectCounter):
                        connectionQuery = true;
                        break;
                    default:
                        throw new Exception("Invalid Operation");
                }

                int retVal = 0;
                HttpWebResponse res = null;

                if (!connectionQuery)
                {
                    try
                    {
                        HttpWebRequest req = (HttpWebRequest)WebRequest.Create(command);
                        res = (HttpWebResponse)req.GetResponse();
                        retVal = int.Parse(new StreamReader(res.GetResponseStream()).ReadLine());
                        res.Close();
                    }
                    catch (Exception e)
                    {
                        logger.Log("{0}", e.Source);
                        logger.Log("{0}", e.StackTrace);
                        logger.Log("{0}", e.Message);
                        try { res.Close(); }
                        catch { }
                    }
                }
                else
                {
                    try
                    {
                        HttpWebRequest req = (HttpWebRequest)WebRequest.Create(isOffQuery);
                        res = (HttpWebResponse)req.GetResponse();
                        retVal = int.Parse(new StreamReader(res.GetResponseStream()).ReadLine());
                        res.Close();
                    }
                    catch (Exception e)
                    {
                        logger.Log("{0}", e.Source);
                        logger.Log("{0}", e.StackTrace);
                        logger.Log("{0}", e.Message);
                        try { res.Close(); }
                        catch { }
                    }
                }
                List<View.VParamType> retVals = new List<View.VParamType>();
                try
                {
                    retVals.Add(new ParamType(ParamType.SimpleType.integer, "32", Convert.ToInt32(retVal), ""));
                }
                catch (Exception e)
                {
                    //logger.Log("Could not convert {0} to bytes", retVal.ToString());
                    logger.Log("{0}", e.Message);
                    retVals.Add(new ParamType(ParamType.SimpleType.integer, "32", 0, ""));
                }

                //Console.WriteLine(Convert.ToByte(retVal));

                return retVals;
            }

            private static string meterService = "urn:micasaverde-com:serviceId:EnergyMetering1";
        }

        //All other ports are irrelevant
        public override void PortRegistered(View.VPort port) { }
        public override void PortDeregistered(View.VPort port) { }

    }
}
